package com.mlnx.mptp.serial;

import com.mlnx.common.utils.MyLog;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Timer;
import java.util.TimerTask;

import gnu.io.SerialPort;

public class Serial implements ISender {

    private MyLog log = MyLog.getLog(getClass());

    private SerialParameters serialParameters;
    private SerialPort serialPort;
    private InputStream recvStream;
    private OutputStream sendStream;
    private boolean open = false; // 串口是否已经打开
    private boolean occupy = false; // 串口损坏

    private IReciver reciver;
    private ReciveDataRunnable reciveDataRunnable;

    private Timer openTimer;


    public Serial(String portName) {
        this(portName, 9600);
    }

    public Serial(String portName, int baudRate) {
        this(portName, baudRate, SerialPort.PARITY_NONE);
    }

    public Serial(String portName, int baudRate, int parity) {
        this(portName, baudRate, parity, SerialPort.FLOWCONTROL_NONE);
    }

    public Serial(String portName, int baudRate, int parity, int flowControlIn) {
        serialParameters = new SerialParameters();
        serialParameters.setPortName(portName);
        serialParameters.setBaudRate(baudRate);
        serialParameters.setParity(parity);
        serialParameters.setFlowControlIn(flowControlIn);
    }

    public Serial(String portName, IReciver reciver) {
        this(portName);
        this.reciver = reciver;
    }

    public Serial(String portName, int baudRate, IReciver reciver) {
        this(portName, baudRate);
        this.reciver = reciver;
    }

    public void setReciver(IReciver reciver) {
        this.reciver = reciver;
    }

    @Override
    public void start() throws IOException {
//        openSerialPort();
        startOpenTimer();
    }

    @Override
    public void stop() {
        if (openTimer != null){
            openTimer.cancel();
        }
        closeSerialPort();
    }

    public boolean openSerialPort() {
        if (open) {
            return true;
        }
        serialPort = SerialFactory.openSerial(serialParameters);
        if (serialPort == null) {
            log.error(getClass().getName() + " serialPort == null openSerial error");
            occupy = true;
            return false;
        }
        printSerialInfo();

        try {
            recvStream = serialPort.getInputStream();
            sendStream = serialPort.getOutputStream();
            startRevice();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }

        open = true;
        occupy = false;

        return true;
    }

    private void startOpenTimer(){
        if (!open){
            if (openTimer != null){
                openTimer.cancel();
            }

            openTimer = new Timer();
            openTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    openSerialPort();
                    if (open){
                        openTimer.cancel();
                    }
                }
            }, 0, 1000);
        }
    }

    private void printSerialInfo(){

        StringBuilder builder = new StringBuilder();
        builder.append("name:"+serialPort.getName()+"\n");
        builder.append("波特率:"+serialPort.getBaudRate()+"\n");
        builder.append("流控:"+serialPort.getFlowControlMode()+"\n");
        builder.append("DataBits:"+serialPort.getDataBits()+"\n");
        builder.append("Parity:"+serialPort.getParity()+"\n");
        builder.append("StopBits:"+serialPort.getStopBits()+"\n");

        log.info(builder.toString());
    }


    public void closeSerialPort() {
        if (!open) {
            return;
        }

        if (serialPort != null) {
            try {
                // close the i/o streams.
                recvStream.close();
                sendStream.close();
                stopRevice();
                // Close the port.
                serialPort.close();
                serialPort = null;
            } catch (IOException e) {
                log.error("关闭串口失败", e);
            }
        }
        open = false;

    }

    private void startRevice() {
        if (reciveDataRunnable == null) {
            reciveDataRunnable = new ReciveDataRunnable(recvStream, reciver);
            new Thread(reciveDataRunnable).start();
        }
    }

    private void stopRevice() {
        if (reciveDataRunnable != null) {
            reciveDataRunnable.stop();
            reciveDataRunnable = null;
        }
    }

    public boolean isOccupy() {
        return occupy;
    }

    public boolean IsOpen() {
        return open;
    }

    public SerialParameters getSerialParameters() {
        return serialParameters;
    }

    public void setSerialParameters(SerialParameters serialParameters) {
        this.serialParameters = serialParameters;
        if (open) {
            SerialFactory.SetSerialParams(serialPort, serialParameters);
        }
    }

    public void setSerialParameters() {
        if (open) {
            SerialFactory.SetSerialParams(serialPort, serialParameters);
        }
    }

    public String getPortName() {
        return serialParameters.getPortName();
    }

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Serial)) {
            return false;
        }
        Serial serial = (Serial) obj;

        if (this.serialParameters.getPortName().equals(serial.getPortName())) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public synchronized void send(byte[] bs, int len) throws IOException {
        sendStream.write(bs);
    }

    public static void main(String[] args) {

        Serial serial1 = new Serial("COM1");
        serial1.openSerialPort();

        Serial serial2 = new Serial("COM8");
        serial2.openSerialPort();

        serial1.closeSerialPort();
        serial2.closeSerialPort();
        serial1 = new Serial("COM1");
        serial1.openSerialPort();
    }

}
